package org.eclipse.epsilon.emc.jdt;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;

/**
 * This is the AST model containing all AST nodes.
 * 
 * @author Cheng Yun
 *
 */
public class ASTModel extends ASTVisitor implements ASTReflection{
	protected List<Object> all = null;
	protected HashMap<String, ArrayList<ASTNode>> astModel;

	public ASTModel(Collection<IJavaProject> projects) throws JavaModelException{
		init(projects);
	}

	public Collection<?> getAllOfKind(String type) throws JavaModelException {
		return astModel.get(type);
	}

	public Collection<?> getAllOfType(String type) throws JavaModelException {
		boolean isAbstract = false;
		//find out if the array list contains two items that are not of the same type
		outer:for(int i=0;i<astModel.get(type).size();i++){
			for(int j=0;j<astModel.get(type).size();j++){
				if(astModel.get(type).get(i).getClass()!=astModel.get(type).get(j).getClass()){
					isAbstract=true;
					break outer;
				}
			}
		}
		//if the array list is empty OR the type is an abstract type
		if(astModel.get(type).isEmpty() || isAbstract){
			return null;
		}
		return astModel.get(type);
	}

	public HashMap<String, ArrayList<ASTNode>> init (Collection<IJavaProject> projects) throws JavaModelException {
		this.astModel = new HashMap<String, ArrayList<ASTNode>>();

		for (IJavaProject project : projects) {
			for (IPackageFragment packageFragment : project
					.getPackageFragments()) {
				if (packageFragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
					for (ICompilationUnit compilationUnit : packageFragment
							.getCompilationUnits()) {
						ASTParser parser = ASTParser.newParser(AST.JLS4);
						parser.setKind(ASTParser.K_COMPILATION_UNIT);
						parser.setSource(compilationUnit);
						// This is slow and may not always be needed
						// Perhaps the driver could provide an option that turns
						// this on/off
						// parser.setResolveBindings(true);
						parser.createAST(null).accept(this);
					}
				}
			}
		}
		return astModel;
	}

	@Override
	public void preVisit(ASTNode node) {
		String key = node.getClass().getSimpleName();
			initMap(key,node);
			Class<?> superClass = node.getClass().getSuperclass();
			while (superClass != Object.class) {
				key = superClass.getSimpleName();
				initMap(key,node);
				superClass = superClass.getSuperclass();
			}
	}
	
	private void initMap(String key,ASTNode node){
		if (astModel.containsKey(key)) {
			astModel.get(key).add(node);
		} else {
			astModel.put(key, new ArrayList<ASTNode>());
			astModel.get(key).add(node);
		}
	}
		
	public HashMap<String, ArrayList<ASTNode>> getAstModel() {
		return astModel;
	}
}

